home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / HAS Other Source / WASTE 1.3a4 Distribution / WASTE 1.3a4 / Source / WEDrawing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-04-04  |  26.8 KB  |  986 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEDrawing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Drawing routines and other basic support functions
  6.  *
  7.  *  Copyright (c) 1993-1997 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14. #include "WASTEIntf.h"
  15.  
  16. #if WASTE_OBJECTS
  17.  
  18. pascal OSErr WEGetSelectedObject(WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  19. {
  20.     WEPtr pWE = *hWE;
  21.     WERunInfo runInfo;
  22.  
  23.     // assume current selection is not an embedded object
  24.     *hObjectDesc = nil;
  25.  
  26.     // check selection range
  27.     if (pWE->selEnd - pWE->selStart == 1)
  28.     {
  29.         // check run info
  30.         WEGetRunInfo(pWE->selStart, &runInfo, hWE);
  31.         if ((*hObjectDesc = runInfo.runAttrs.runStyle.tsObject) != nil)
  32.         {
  33.             return noErr;
  34.         }
  35.     }
  36.     return weObjectNotFoundErr;
  37. }
  38.  
  39. pascal SInt32 WEFindNextObject(SInt32 offset, WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  40. {
  41.     WEPtr pWE = *hWE;
  42.     WEStyleTableEntry *pStyles = *pWE->hStyles;
  43.     WEObjectDescHandle obj;
  44.     WERunArrayEntry *pRun;
  45.  
  46.     *hObjectDesc = nil;
  47.  
  48.     // do nothing if offset is already at the end of the text
  49.     if (offset >= pWE->textLength - 1)
  50.     {
  51.         return kInvalidOffset;
  52.     }
  53.  
  54.     // get a pointer to the run array entry immediately following offset
  55.     pRun = *pWE->hRuns + WEOffsetToRun(offset + 1, hWE);
  56.  
  57.     // perform a linear scan of the run array looking for a run whose
  58.     // corresponding style table entry points to an embedded object;
  59.     // the search will stop anyway because the last run array entry has styleIndex = -1
  60.     while (pRun->styleIndex >= 0)
  61.     {
  62.         if ((obj = pStyles[pRun->styleIndex].info.runStyle.tsObject) != nil)
  63.         {
  64.             *hObjectDesc = obj;
  65.             return pRun->runStart;
  66.         }
  67.         pRun++;
  68.     }
  69.  
  70.     return kInvalidOffset;
  71.  
  72. }
  73.  
  74. #endif  // WASTE_OBJECTS
  75.  
  76. pascal void _WEContinuousStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode *mode, WETextStyle *ts, WEHandle hWE)
  77. {
  78.     // find out which style attributes are continous over the specified text range
  79.     // on entry, *mode specifies which attributes are to be checked
  80.     // on exit, *mode specifies the continuous attributes, also copied to ts
  81.  
  82.     WEPtr pWE = *hWE;
  83.     SInt32 runIndex;
  84.     WEStyleMode outMode;
  85.     WERunInfo runInfo;
  86.  
  87.     outMode = *mode;
  88.  
  89.     // get style info at the beginning of the specified range
  90.     runIndex = WEOffsetToRun(rangeStart, hWE);
  91.     _WEGetIndStyle(runIndex, &runInfo, hWE);
  92.  
  93.     // copy the specified fields to ts
  94.     _WECopyStyle(&runInfo.runAttrs.runStyle, ts, normal, (outMode & weDoAll) | weDoReplaceFace);
  95.  
  96.     // loop through style runs across the current selection range
  97.     // if we determine that all specified attributes are discontinuous, we exit prematurely
  98.     do
  99.     {
  100.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  101.  
  102.         // determine which attributes have changed, if any
  103.         if (outMode & weDoFont)
  104.         {
  105.             if (runInfo.runAttrs.runStyle.tsFont != ts->tsFont)
  106.             {
  107.                 outMode &= (~weDoFont);
  108.             }
  109.         }
  110.         if (outMode & weDoFace)
  111.         {
  112.             if (runInfo.runAttrs.runStyle.tsFace != ts->tsFace)
  113.             {
  114.                 ts->tsFace &= runInfo.runAttrs.runStyle.tsFace;
  115.                 if (ts->tsFace == 0)
  116.                 {
  117.                     outMode &= (~weDoFace);
  118.                 }
  119.             }
  120.         }
  121.         if (outMode & weDoFaceMask)
  122.         {
  123.             ts->tsFlags |= runInfo.runAttrs.runStyle.tsFace;
  124.         }
  125.         if (outMode & weDoSize)
  126.         {
  127.             if (runInfo.runAttrs.runStyle.tsSize != ts->tsSize)
  128.             {
  129.                 outMode &= (~weDoSize);
  130.             }
  131.         }
  132.         if (outMode & weDoColor)
  133.         {
  134.             if (!_WEBlockCmp(&runInfo.runAttrs.runStyle.tsColor, &ts->tsColor, sizeof(RGBColor)))
  135.             {
  136.                 outMode &= (~weDoColor);
  137.             }
  138.         }
  139.  
  140.         runIndex++;
  141.     } while ((outMode != 0) && (runInfo.runEnd < rangeEnd));
  142.  
  143.     *mode = outMode;
  144. }
  145.  
  146. pascal void _WESynchNullStyle(WEHandle hWE)
  147. {
  148.     // This routine fills the nullStyle field of the WE record with valid information
  149.     // and makes sure that the null style font belongs to the keyboard script.
  150.  
  151.     WEPtr pWE = *hWE;    // assume WE record is already locked
  152.     SInt32 runIndex;
  153.     WERunInfo runInfo;
  154. #if !WASTE_NO_SYNCH
  155.     ScriptCode keyboardScript;
  156.     SInt16 fontID;
  157. #endif
  158.  
  159.     // find the run index of the style run preceding the insertion point
  160.     runIndex = WEOffsetToRun(pWE->selStart - 1, hWE);
  161.  
  162.     // if the nullStyle record is marked as invalid, fill it with the style attributes
  163.     // associated with the character preceding the insertion point, and mark it as valid
  164.     if (!BTST(pWE->flags, weFUseNullStyle))
  165.     {
  166.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  167.         pWE->nullStyle = runInfo.runAttrs;
  168.         BSET(pWE->flags, weFUseNullStyle);
  169.     }
  170.  
  171. #if !WASTE_NO_SYNCH
  172.     // if only the Roman script is installed, we're finished
  173.     if (!BTST(pWE->flags, weFNonRoman))
  174.     {
  175.         return;
  176.     }
  177.  
  178.     // *** FONT / KEYBOARD SYNCHRONIZATION ***
  179.     // get the keyboard script
  180.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  181.  
  182.     // find out what font will be used for the next character typed
  183.     fontID = pWE->nullStyle.runStyle.tsFont;
  184.  
  185.     // do nothing if the font script is the same as the keyboard script
  186.     if (FontToScript(fontID) == keyboardScript)
  187.     {
  188.         return;
  189.     }
  190.  
  191.     // scan style runs starting from the insertion point backwards,
  192.     // looking for the first font belonging to the keyboard script
  193.     do
  194.     {
  195.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  196.         fontID = runInfo.runAttrs.runStyle.tsFont;
  197.         if (FontToScript(fontID) == keyboardScript)
  198.         {
  199.             break;
  200.         }
  201.         runIndex--;
  202.     } while (runIndex>=0);
  203.  
  204.     // if no font was ever used for the keyboard script, default to the
  205.     // application font for the script
  206.     if (runIndex < 0)
  207.     {
  208.         fontID = GetScriptVariable(keyboardScript, smScriptAppFond);
  209.     }
  210.  
  211.     // change the font in the null style record
  212.     pWE->nullStyle.runStyle.tsFont = fontID;
  213. #endif
  214. }
  215.  
  216. pascal Boolean WEContinuousStyle(WEStyleMode *mode, TextStyle *ts, WEHandle hWE)
  217. {
  218.     // find out which style attributes are continous over the selection range
  219.     // on entry, the mode bitmap specifies which attributes are to be checked
  220.     // on exit, the mode bitmap specifies the continuous attributes, also copied to ts
  221.     // return true if all specified attributes are continuous
  222.  
  223.     WEPtr pWE;
  224.     WEStyleMode oldMode;
  225.     Boolean continuousStyle;
  226.     Boolean saveWELock;
  227.  
  228.     // lock the WE record
  229.     pWE = *hWE;
  230.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  231.  
  232.     // mask out unused bits in *mode
  233.     *mode &= (weDoAll | weDoFaceMask);
  234.  
  235.     // two rather different paths are taken depending on whether
  236.     // the selection range is empty or not
  237.     if (pWE->selStart == pWE->selEnd)
  238.     {
  239.         // if the selection range is empty, always return true and set ts
  240.         // from the nullStyle record, after having validated it
  241.         continuousStyle = true;
  242.         _WESynchNullStyle(hWE);
  243.         _WECopyStyle(&pWE->nullStyle.runStyle, (WETextStyle *) ts, normal, (*mode & weDoAll) | weDoReplaceFace);
  244.     }
  245.     else
  246.     {
  247.         // otherwise get the continuous style attributes over the selection range
  248.         oldMode = *mode;
  249.         _WEContinuousStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *) ts, hWE);
  250.  
  251.         // return true if mode hasn't changed
  252.         continuousStyle = (oldMode == *mode);
  253.     }
  254.  
  255.     // unlock the WE record
  256.     _WESetHandleLock((Handle) hWE, saveWELock);
  257.  
  258.     return continuousStyle;
  259. }
  260.  
  261. static pascal Boolean GetSegmentDirection(SInt16 segmentIndex, const struct FormatOrderData *data)
  262. {
  263.     return _WEGetIndDirection(data->firstRun + segmentIndex, data->hWE);
  264. }
  265.  
  266. pascal void _WESegmentLoop(SInt32 firstLine, SInt32 lastLine, WESegmentLoopProcPtr callback, void *callbackData, WEHandle hWE)
  267. {
  268.     // For each style segment on every line in the specified range, set up
  269.     // text attributes in the port and call the callback.
  270.     // the WE record must be already locked
  271.  
  272.     WEPtr pWE = *hWE;
  273.     WELineRec *pLine;
  274.     Ptr pText;
  275.     SInt32 lineIndex;
  276.     SInt32 firstRun;
  277.     SInt32 segmentIndex, segmentCount;
  278.     SInt32 runIndex, currentRunIndex;
  279.     SInt32 lineStart, lineEnd, segmentStart, segmentEnd;
  280.     JustStyleCode styleRunPosition;
  281.     WERunInfo runInfo;
  282.     SInt16 autoOrdering[ kAutoOrderingSize ];
  283.     Handle hTempOrdering = nil;
  284.     SInt16 *pOrdering;
  285.     struct FormatOrderData formatOrderData;
  286.     Boolean isRightToLeft;
  287.     Boolean saveLineLock;
  288.     Boolean saveTextLock;
  289.     QDEnvironment saveEnvironment;
  290.  
  291.     static StyleRunDirectionUPP directionProc = nil;
  292.  
  293.     // create a routine descriptor for GetSegmentDirection, if we haven't already
  294.     if (directionProc == nil)
  295.     {
  296.         directionProc = NewStyleRunDirectionProc(&GetSegmentDirection);
  297.     }
  298.  
  299.     // is right-to-left the dominant line direction?
  300.     isRightToLeft = IsRightToLeft(pWE->direction);
  301.  
  302.     // save the Quickdraw environment
  303.     _WESaveQDEnvironment(pWE->port, BTST(pWE->flags, weFHasColorQD) ? true : false, &saveEnvironment);
  304.  
  305.     // make sure firstLine and lastLine are within the allowed range
  306.     lineIndex = pWE->nLines - 1;
  307.     firstLine = _WEPinInRange(firstLine, 0, lineIndex);
  308.     lastLine = _WEPinInRange(lastLine, 0, lineIndex);
  309.  
  310.     // lock the line array
  311.     saveLineLock = _WESetHandleLock((Handle) pWE->hLines, true);
  312.     pLine = *pWE->hLines + firstLine;
  313.  
  314.     // lock the text
  315.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  316.     pText = *pWE->hText;
  317.  
  318.     currentRunIndex = -1;
  319.  
  320.     // loop thru the specified lines
  321.     for ( lineIndex = firstLine; lineIndex <= lastLine; lineIndex++ )
  322.     {
  323.         // get line start and line end
  324.         lineStart = pLine[0].lineStart;
  325.         lineEnd = pLine[1].lineStart;
  326.  
  327.         // calculate the index to the first style run on the line
  328.         firstRun = WEOffsetToRun(lineStart, hWE);
  329.  
  330.         // calculate the number of segments on this line
  331.         segmentCount = WEOffsetToRun(lineEnd - 1, hWE) - firstRun + 1;
  332.  
  333.         if (BTST(pWE->flags, weFBidirectional))
  334.         {
  335.             // SPECIAL PROCESSING FOR BIDIRECTIONAL SCRIPTS
  336.             // we need an array of segmentCount entries for GetFormatOrder
  337.             // in most cases, stack storage will be sufficient
  338.             if (segmentCount <= kAutoOrderingSize)
  339.             {
  340.                 pOrdering = autoOrdering;
  341.             }
  342.             else
  343.             {
  344.                 // in rare cases the ordering array must be allocated dynamically
  345.                 _WEForgetHandle(&hTempOrdering);
  346.                 if (_WEAllocate(segmentCount * sizeof(SInt16), kAllocTemp, &hTempOrdering) != noErr)
  347.                 {
  348.                     // couldn't allocate memory: emergency exit
  349.                     break;
  350.                 }
  351.                 HLock(hTempOrdering);
  352.                 pOrdering = (SInt16 *) *hTempOrdering;
  353.             }
  354.  
  355.             // obtain the correct display order for the segments
  356.             if (segmentCount > 1)
  357.             {
  358.                 formatOrderData.firstRun = firstRun;
  359.                 formatOrderData.hWE = hWE;
  360.                 GetFormatOrder((FormatOrderPtr) pOrdering, 0, segmentCount - 1, isRightToLeft, directionProc, (Ptr) &formatOrderData);
  361.             }
  362.             else
  363.             {
  364.                 pOrdering[0] = 0;
  365.             }
  366.         }
  367.  
  368.         // loop thru each segment on this line
  369.         for ( segmentIndex = 0; segmentIndex < segmentCount; segmentIndex++ )
  370.         {
  371.             if (BTST(pWE->flags, weFBidirectional))
  372.             {
  373.                 runIndex = pOrdering[ segmentIndex ];
  374.             }
  375.             else
  376.             {
  377.                 runIndex = segmentIndex;
  378.             }
  379.             runIndex += firstRun;
  380.  
  381.             if (currentRunIndex != runIndex)
  382.             {
  383.                 // get style run information for the current style run
  384.                 _WEGetIndStyle(runIndex, &runInfo, hWE);
  385.  
  386.                 // set new text attributes
  387.                 TextFont(runInfo.runAttrs.runStyle.tsFont);
  388.                 TextFace(runInfo.runAttrs.runStyle.tsFace);
  389.                 TextSize(runInfo.runAttrs.runStyle.tsSize);
  390.  
  391.                 // remember current run index
  392.                 currentRunIndex = runIndex;
  393.             }
  394.  
  395.             // determine segment boundaries
  396.             segmentStart = (runInfo.runStart <= lineStart) ? lineStart : runInfo.runStart;
  397.             segmentEnd = (runInfo.runEnd >= lineEnd) ? lineEnd : runInfo.runEnd;
  398.  
  399.             // determine the relative position of this segment on the line
  400.             styleRunPosition = 0;        // onlyStyleRun
  401.             if (segmentIndex < segmentCount - 1)
  402.             {
  403.                 styleRunPosition += 1;    // leftStyleRun or middleStyleRun
  404.             }
  405.             if (segmentIndex > 0)
  406.             {
  407.                 styleRunPosition += 2;    // rightStyleRun or middleStyleRun
  408.             }
  409.  
  410.             // do the callback
  411.             if (callback(pLine, &runInfo.runAttrs, pText + segmentStart, segmentStart, segmentEnd - segmentStart, styleRunPosition, hWE, callbackData))
  412.             {
  413.                 break;
  414.             }
  415.         };
  416.         pLine++;
  417.     }
  418.  
  419.     // unlock the text
  420.     _WESetHandleLock(pWE->hText, saveTextLock);
  421.  
  422.     // unlock the line array
  423.     _WESetHandleLock((Handle) pWE->hLines, saveLineLock);
  424.  
  425.     // restore the Quickdraw environment
  426.     _WERestoreQDEnvironment(&saveEnvironment);
  427.  
  428.     // dispose of any temporary storage
  429.     _WEForgetHandle(&hTempOrdering);
  430. }
  431.  
  432. pascal void _WEDrawTSMHilite(Rect *segmentRect, UInt8 tsFlags)
  433. {
  434.     SInt16 underlineHeight;
  435.     RGBColor background, foreground, saveForeground;
  436.     Boolean isColorPort;
  437.     Boolean usingTrueGray;
  438.  
  439.     isColorPort = (((CGrafPtr) qd.thePort)->portVersion < 0);
  440.     usingTrueGray = false;
  441.  
  442.     // by default, the pen pattern is solid
  443.     PenPat(&qd.black);
  444.  
  445.     // if we're drawing in color, set the foreground color
  446.     if (isColorPort)
  447.     {
  448.         // save foreground color
  449.         GetForeColor(&saveForeground);
  450.  
  451.         // by default, the foreground color is black
  452.         foreground.red = 0;
  453.         foreground.green = 0;
  454.         foreground.blue = 0;
  455.  
  456.         // if we're underlining raw (unconverted) text, see if a "true gray" is available
  457.         if (!BTST(tsFlags, tsTSMConverted))
  458.         {
  459.             GetBackColor(&background);
  460.             usingTrueGray = GetGray(GetGDevice(), &background, &foreground);
  461.         } // if raw text
  462.  
  463.         // set the foreground color
  464.         RGBForeColor(&foreground);
  465.     } // if color graf port
  466.  
  467.     // if we're underlining raw (unconverted) text and no true gray is available,
  468.     // simulate gray with a 50% pattern
  469.     if (!BTST(tsFlags, tsTSMConverted))
  470.     {
  471.         if (!usingTrueGray)
  472.         {
  473.             PenPat(&qd.gray);
  474.         }
  475.     }
  476.     // use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline
  477.     underlineHeight = BTST(tsFlags, tsTSMSelected) ? 2 : 1;
  478.  
  479.     // segmentRect becomes the rectangle to paint
  480.     InsetRect(segmentRect, 1, 0);
  481.     segmentRect->top = segmentRect->bottom - underlineHeight;
  482.  
  483.     // draw the underline
  484.     PaintRect(segmentRect);
  485.  
  486.     // restore the foreground color
  487.     if (isColorPort)
  488.     {
  489.         RGBForeColor(&saveForeground);
  490.     }
  491. }
  492.  
  493. static Boolean SLDraw
  494.     (
  495.         WELineRec *pLine,
  496.         const WERunAttributes *pAttrs,
  497.         Ptr pSegment,
  498.         SInt32 segmentStart,
  499.         SInt32 segmentLength,
  500.         JustStyleCode styleRunPosition,
  501.         WEHandle hWE,
  502.         void *callbackData
  503.     )
  504. {
  505. #pragma unused(segmentStart)
  506.     struct SLDrawData *cd = (struct SLDrawData *) callbackData;
  507.     WEPtr pWE = *hWE;
  508.     Fixed slop;
  509.     Rect segmentRect;
  510.     RGBColor rgbTemp1, rgbTemp2;
  511.  
  512.     // is this the first segment on this line?
  513.     if (IS_LEFTMOST_RUN(styleRunPosition))
  514.     {
  515.         // calculate the line rectangle (the rectangle which completely encloses the current line)
  516.         cd->lineRect.left = pWE->destRect.left;
  517.         cd->lineRect.right = pWE->destRect.right;
  518.         cd->lineRect.top = pWE->destRect.top + pLine[0].lineOrigin;
  519.         cd->lineRect.bottom = pWE->destRect.top + pLine[1].lineOrigin;
  520.  
  521.         // calculate the visible portion of this rectangle
  522.         // we do this by intersecting the line rectangle with the view rectangle
  523.         cd->drawRect = (*pWE->viewRgn)->rgnBBox;
  524.         SectRect(&cd->lineRect, &cd->drawRect, &cd->drawRect);
  525.  
  526.         if (cd->usingOffscreen)
  527.         {
  528.             // calculate the boundary rectangle for the offscreen buffer
  529.             // this is simply drawRect converted to global coordinates
  530.             cd->bounds = cd->drawRect;
  531.             LocalToGlobal((Point *) &cd->bounds.top);
  532.             LocalToGlobal((Point *) &cd->bounds.bottom);
  533.  
  534.             // update the offscreen graphics world for the new bounds (this could fail)
  535.             cd->drawingOffscreen = false;
  536.             if (! (UpdateGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd->bounds, nil, nil, 0) & gwFlagErr))
  537.             {
  538.                 // NOTE: when running on a 68000 machine with the original Quickdraw,
  539.                 // a GWorld is just an extended GrafPort, and GetGWorldPixMap actually
  540.                 // returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap).
  541.                 // An important side-effect of this is that when we call SetOrigin,
  542.                 // only the original portBits is offset, not the copy.
  543.                 // get the pixel map associated with the offscreen graphics world
  544.                 cd->offscreenPixels = GetGWorldPixMap((GWorldPtr) pWE->offscreenPort);
  545.  
  546.                 // lock it down
  547.                 if (LockPixels(cd->offscreenPixels))
  548.                 {
  549.                     // offscreen pixel buffer allocation was successful
  550.                     cd->drawingOffscreen = true;
  551.  
  552.                     // get background color of onscreen port
  553.                     GetBackColor(&rgbTemp1);
  554.  
  555.                     // switch graphics world
  556.                     SetGWorld((GWorldPtr) pWE->offscreenPort, nil);
  557.  
  558.                     // synchronize the coordinate system of the offscreen port with that of the screen port
  559.                     SetOrigin(cd->drawRect.left, cd->drawRect.top);
  560.  
  561.                     // synchronize the background color
  562.                     RGBBackColor(&rgbTemp1);
  563.  
  564.                     // reset the offscreen clip region
  565.                     ClipRect(&cd->drawRect);
  566.                 }
  567.             } // if pixel buffer allocation was successful
  568.         } // if usingOffscreen
  569.  
  570.         // if doErase is true, erase the drawable area before drawing text
  571.         if (cd->doErase)
  572.         {
  573.             CallWEEraseProc(&cd->drawRect, hWE, pWE->eraseHook);
  574.         }
  575.  
  576.         // position the pen
  577.         MoveTo(cd->lineRect.left + _WECalcPenIndent(pLine, pWE->alignment, pWE->direction), cd->lineRect.top + pLine->lineAscent);
  578.     } // if first segment on line
  579.  
  580.     // if drawingOffscreen, switch thePort to the offscreen port
  581.     // and synchronize text attributes
  582.     if (cd->drawingOffscreen)
  583.     {
  584.         SetPort(pWE->offscreenPort);
  585.         TextFont(pAttrs->runStyle.tsFont);
  586.         TextFace(pAttrs->runStyle.tsFace);
  587.         TextSize(pAttrs->runStyle.tsSize);
  588.     } // if drawingOffscreen
  589.  
  590.     // get horizontal coordinate of the pen before drawing the segment
  591.     GetPen((Point *) &segmentRect.top);
  592.  
  593.     // set the foreground color
  594.     if (cd->usingColor)
  595.     {
  596.         RGBForeColor(&pAttrs->runStyle.tsColor);
  597.     }
  598.  
  599. #if WASTE_OBJECTS
  600.     if (pAttrs->runStyle.tsObject != nil)
  601.     {
  602.         _WEDrawObject(pAttrs->runStyle.tsObject);
  603.     }
  604.     else
  605. #endif
  606.     {
  607.         slop = 0;
  608.  
  609.         // calculate the "slop" (extra space) for this text segment (justified text only)
  610.         if (pWE->alignment == weJustify)
  611.         {
  612.             // if this is the last segment on the line, strip trailing spaces
  613.             if (IS_RIGHTMOST_RUN(styleRunPosition))
  614.             {
  615.                 segmentLength = VisibleLength(pSegment, segmentLength);
  616.             }
  617.             // calculate how much extra space is to be applied to this text segment
  618.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  619.                     kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  620.  
  621.         } // if (alignment == weJustify)
  622.  
  623.         // draw the segment
  624.         CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, hWE, pWE->drawTextHook);
  625.     }
  626.  
  627.     // get horizontal coordinate of the pen after drawing the segment
  628.     GetPen((Point *) &segmentRect.bottom);
  629.     segmentRect.bottom = cd->lineRect.bottom;
  630.  
  631.     // if this segment is in the TSM area, underline it in the appropriate way
  632.     if (BTST(pAttrs->runStyle.tsFlags, tsTSMHilite))
  633.     {
  634.         _WEDrawTSMHilite(&segmentRect, pAttrs->runStyle.tsFlags);
  635.     }
  636.  
  637.     if (cd->drawingOffscreen)
  638.     {
  639.         if (IS_RIGHTMOST_RUN(styleRunPosition))
  640.         {
  641.             // after drawing offscreen the last segment,
  642.             // prepare to copy the offscreen buffer to video RAM
  643.  
  644.             // first set the graphics world to the screen port
  645.             SetGWorld((CGrafPtr) cd->screenPort, cd->screenDevice);
  646.  
  647.             // before calling CopyBits, set the foreground color to black
  648.             // and the background to white to avoid colorization
  649.             if (cd->usingColor)
  650.             {
  651.                 GetBackColor(&rgbTemp1);
  652.                 rgbTemp2.red = 0xFFFF;
  653.                 rgbTemp2.green = 0xFFFF;
  654.                 rgbTemp2.blue = 0xFFFF;
  655.                 RGBBackColor(&rgbTemp2);
  656.                 rgbTemp2.red = 0;
  657.                 rgbTemp2.green = 0;
  658.                 rgbTemp2.blue = 0;
  659.                 RGBForeColor(&rgbTemp2);
  660.             }
  661.  
  662.             // copy the offscreen image of the [visible portion of the] line to the screen
  663.             CopyBits(&pWE->offscreenPort->portBits, &cd->screenPort->portBits,
  664.                      &cd->drawRect, &cd->drawRect, srcCopy, nil);
  665.  
  666.             // restore the original background color in the onscreen port
  667.             if (cd->usingColor)
  668.             {
  669.                 RGBBackColor(&rgbTemp1);
  670.             }
  671.  
  672.             // restore the original offscreen coordinate system and unlock the pixel image
  673.             SetPort(pWE->offscreenPort);
  674.             SetOrigin(0, 0);
  675.             if (cd->usingColor)
  676.             {
  677.                 RGBForeColor(&rgbTemp2);
  678.             }
  679.             UnlockPixels(cd->offscreenPixels);
  680.  
  681.         } // if last segment
  682.  
  683.         // restore the screen port for _WESegmentLoop
  684.         SetPort(cd->screenPort);
  685.     } // if drawingOffscreen
  686.  
  687.     return false;    // keep looping
  688. }
  689.  
  690. pascal void _WEDrawLines (SInt32 firstLine, SInt32 lastLine, Boolean doErase, WEHandle hWE)
  691. {
  692.     // draw the specified range of lines
  693.     // we can safely assume that the WE record is already locked
  694.     // and the port is already set the pWE->port
  695.  
  696.     WEPtr pWE = *hWE;            // assume WE record is locked
  697.     struct SLDrawData cd;        // block of data we'll pass to SLDraw
  698.  
  699.     BLOCK_CLR(cd);
  700.     cd.doErase = doErase;
  701.     cd.usingColor = (BTST(pWE->flags, weFHasColorQD) && !BTST(pWE->features, weFInhibitColor)) ? true : false;
  702.  
  703.     // do nothing if our graphics port is not visible
  704.     if (EmptyRgn(pWE->port->visRgn))
  705.     {
  706.         return;
  707.     }
  708.  
  709.     // save graphics world
  710.     GetGWorld((GWorldPtr *) &cd.screenPort, &cd.screenDevice);
  711.  
  712.     // If doErase is true, we're drawing over old text, so we must erase each line
  713.     // before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw
  714.     // the entire line offscreen and  we copy the image right over the old line,
  715.     // without erasing it, thus achieving a very smooth drawing effect.
  716.  
  717.     if ((doErase) && BTST(pWE->features, weFDrawOffscreen))
  718.     {
  719.         // has an offscreen world already been allocated?
  720.         if (pWE->offscreenPort == nil)
  721.         {
  722.             // nope,  create one; its bounds are set initially to an arbitrary rectangle
  723.             SetRect(&cd.bounds, 0, 0, 1, 1);
  724.  
  725.             // if NewGWorld fails, it will set pWE->offscreenPort to nil
  726.             NewGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd.bounds, nil, nil, pixPurge + noNewDevice + useTempMem);
  727.         }
  728.         cd.usingOffscreen = (pWE->offscreenPort != nil);
  729.     }
  730.  
  731.     _WESegmentLoop(firstLine, lastLine, SLDraw, &cd, hWE);
  732.  
  733.     // restore graphics world
  734.     SetGWorld((GWorldPtr) cd.screenPort, cd.screenDevice);
  735. }
  736.  
  737. pascal SInt16 _WECalcPenIndent(const WELineRec *pLine, WEAlignment alignment, WEDirection direction)
  738. {
  739.     SInt16 retval = 0;
  740.  
  741.     switch (alignment)
  742.     {
  743.         case weFlushLeft:
  744.         {
  745.             break;
  746.         }
  747.  
  748.         case weFlushRight:
  749.         {
  750.             retval = pLine->lineSlop;
  751.             break;
  752.         }
  753.  
  754.         case weCenter:
  755.         {
  756.             retval = pLine->lineSlop / 2;
  757.             break;
  758.         }
  759.  
  760.         case weJustify:
  761.         {
  762.             if (pLine->lineJustAmount > 0)
  763.             {
  764.                 break;
  765.             }
  766.             // deliberate fall through to default case for
  767.             // last line of justified paragraph
  768.         }
  769.  
  770.         default:
  771.         {
  772.             if (IsRightToLeft(direction))
  773.             {
  774.                 retval = pLine->lineSlop;
  775.             }
  776.             break;
  777.         }
  778.     }
  779.  
  780.     return retval;
  781. }
  782.  
  783. pascal void _WESaveQDEnvironment(GrafPtr port, Boolean saveColor, QDEnvironment *environment)
  784. {
  785.     GetPort(&environment->envPort);
  786.     SetPort(port);
  787.     GetPenState(&environment->envPen);
  788.     PenNormal();
  789.     environment->envStyle.tsFont = port->txFont;
  790.     environment->envStyle.tsFace = port->txFace;
  791.     environment->envStyle.tsFlags = saveColor;        // remember if color was saved
  792.     environment->envStyle.tsSize = port->txSize;
  793.     if (saveColor)
  794.     {
  795.         GetForeColor(&environment->envStyle.tsColor);
  796.     }
  797.     environment->envMode = port->txMode;
  798.     TextMode(srcOr);
  799. }
  800.  
  801. pascal void _WERestoreQDEnvironment(const QDEnvironment *environment)
  802. {
  803.     SetPenState(&environment->envPen);
  804.     TextFont(environment->envStyle.tsFont);
  805.     TextFace(environment->envStyle.tsFace);
  806.     TextSize(environment->envStyle.tsSize);
  807.     TextMode(environment->envMode);
  808.     if (environment->envStyle.tsFlags)
  809.     {
  810.         RGBForeColor(&environment->envStyle.tsColor);
  811.     }
  812.     SetPort(environment->envPort);
  813. }
  814.  
  815. pascal void _WEFillFontInfo (GrafPtr port, WERunAttributes *targetStyle)
  816. {
  817.     // given a WERunAttributes record, fill in the runHeight, runAscent fields etc.
  818.     FontInfo fInfo;
  819.     QDEnvironment saveEnvironment;
  820.  
  821.     _WESaveQDEnvironment(port, false, &saveEnvironment);
  822.  
  823.     // we don't want a zero font size; although Quickdraw accepts zero to mean
  824.     // the default font size, it can cause trouble to us when we do calculations
  825.     if (targetStyle->runStyle.tsSize == 0)
  826.     {
  827.         targetStyle->runStyle.tsSize = 12;
  828.     }
  829.  
  830.     // set the text attributes
  831.     TextFont(targetStyle->runStyle.tsFont);
  832.     TextSize(targetStyle->runStyle.tsSize);
  833.     TextFace(targetStyle->runStyle.tsFace);
  834.     GetFontInfo(&fInfo);
  835.     targetStyle->runHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  836.     targetStyle->runAscent = fInfo.ascent;
  837.  
  838.     _WERestoreQDEnvironment(&saveEnvironment);
  839. }
  840.  
  841. pascal void _WECopyStyle(const WETextStyle *sourceStyle, WETextStyle *targetStyle,
  842.                 Style offStyles, WEStyleMode mode)
  843. {
  844.     // Copy some or all of the attributes composing sourceStyle to targetStyle.
  845.     // The mode parameter determines which attributes are to be copied and how.
  846.     // If mode contains weDoToggleFace,  offStyles indicates which
  847.     // Quickdraw styles are to be turned off.
  848.  
  849.     // COPY FONT
  850.     if (mode & weDoFont)
  851.     {
  852.             targetStyle->tsFont = sourceStyle->tsFont;
  853. #if WASTE_RESOLVE_FONT_DESIGNATORS
  854.             if (targetStyle->tsFont == systemFont)
  855.             {
  856.                 targetStyle->tsFont = GetSysFont();
  857.             }
  858.             if (targetStyle->tsFont == applFont)
  859.             {
  860.                 targetStyle->tsFont = GetAppFont();
  861.             }
  862. #endif
  863.     }
  864.  
  865.     // COPY SIZE
  866.     if (mode & (weDoSize | weDoAddSize))
  867.     {
  868.         // copy size to a long variable to avoid integer overflows when doing additions
  869.         SInt32 longSize = sourceStyle->tsSize;
  870.  
  871.         // zero really means 12
  872.         if (longSize == 0)
  873.         {
  874.             longSize = 12;
  875.         }
  876.  
  877.         // if kModeAddSize is set, the source size is added to the target size,
  878.         // otherwise the source size replaces the target size outright
  879.         if (mode & weDoAddSize)
  880.         {
  881.             longSize += targetStyle->tsSize;
  882.         }
  883.         // range-check the resulting size
  884.         longSize = _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  885.         targetStyle->tsSize = longSize;
  886.     } // if alter size
  887.  
  888.     // COPY FACE
  889.     if (mode & (weDoFace | weDoFaceMask))
  890.     {
  891.         Style sourceFace = sourceStyle->tsFace;
  892.         Style targetFace = targetStyle->tsFace;
  893.  
  894.         if (mode & weDoFaceMask)
  895.         {
  896.             // USE MASK
  897.             // if kModeFaceMask is set, copy the Quickdraw styles (tsFace field)
  898.             // using the tsFlags field as a mask specifying which bits in the tsFace
  899.             // field are to be copied.
  900.             Style sourceMask = sourceStyle->tsFlags;
  901.             targetFace = (sourceFace & sourceMask) | (targetFace & (~sourceMask));
  902.         }
  903.         else
  904.         {
  905.             // IGNORE MASK
  906.             // sourceFace replaces targetFace outright if one or both of these conditions hold:
  907.             // 1. sourceFace is zero (= empty set = plain text)
  908.             // 2. the kModeReplaceFace bit is set
  909.  
  910.             if ((sourceFace == normal) || (mode & weDoReplaceFace))
  911.             {
  912.                 targetFace = sourceFace;
  913.             }
  914.             else
  915.             {
  916.                 // Otherwise sourceFace is interpreted as a bitmap indicating
  917.                 // which styles are to be altered -- all other styles are left intact.
  918.                 // What exactly happens to the styles indicated in sourceFace
  919.                 // depends on whether the kModeToggleFace bit is set or clear.
  920.  
  921.                 // if kModeToggleFace is set, turn a style off if it's set in offStyles,
  922.                 // else turn it on
  923.                 if (mode & weDoToggleFace)
  924.                 {
  925.                     targetFace = (sourceFace ^ offStyles) | (targetFace & (~sourceFace));
  926.                 }
  927.                 else
  928.                 {
  929.                     // if kModeToggleFace is clear, turn on the styles specified in sourceStyle
  930.                     targetFace |= sourceFace;
  931.                 }
  932.             }
  933.         }
  934.  
  935.         // the condense and extend attributes are mutually exclusive: if one is set
  936.         // in sourceFace, remove it from targetFace
  937.         if (sourceFace & condense)
  938.         {
  939.             targetFace &= (~extend);
  940.         }
  941.         if (sourceFace & extend)
  942.         {
  943.             targetFace &= (~condense);
  944.         }
  945.  
  946.         targetStyle->tsFace = targetFace;
  947.     }
  948.  
  949.     // COPY COLOR
  950.     if (mode & weDoColor)
  951.     {
  952.         targetStyle->tsColor = sourceStyle->tsColor;
  953.     }
  954.  
  955. #if WASTE_OBJECTS
  956.     // if kModeObject is set, copy object descriptor
  957.     if (mode & weDoObject)
  958.     {
  959.         targetStyle->tsObject = sourceStyle->tsObject;
  960.     }
  961. #endif
  962.  
  963.     // always clear targetStyle->tsFlags by default
  964.     targetStyle->tsFlags = 0;
  965.  
  966.     // if kModeFlags is set, copy the tsFlags field
  967.     if (mode & weDoFlags)
  968.     {
  969.         targetStyle->tsFlags = sourceStyle->tsFlags;
  970.     }
  971. }
  972.  
  973. pascal Boolean _WEOffsetInRange(SInt32 offset, WEEdge edge, SInt32 rangeStart, SInt32 rangeEnd)
  974. {
  975.     // return true if the position specified by the pair < offset, edge >
  976.     // is within the specified range
  977.  
  978.     // if edge is kTrailingEdge, offset really refers to the preceding character
  979.     if (edge == kTrailingEdge)
  980.     {
  981.         offset--;
  982.     }
  983.     // return true iff offset is within the specified range
  984.     return ((offset >= rangeStart) && (offset < rangeEnd));
  985. }
  986.